package demo4

import (
	"fmt"
)

/*
	接口和类型之间的转换
	1. Go语言中使用接口断言（type assertions）将接口转换成另外一个接口，也可以将接口转换为另外的类型。
		接口的转换在开发中非常常见，使用也非常频繁。
	2. 类型断言是一个使用在接口值上的操作。语法上它看起来像 i.(T) 被称为断言类型，这里 i 表示一个接口的类型和 T 表示一个类型。
		一个类型断言检查它操作对象的动态类型是否和断言的类型匹配。
	3. 类型断言的基本格式如下：
			t := i.(T)
		其中，i 代表接口变量，T 代表转换的目标类型，t 代表转换后的变量。
	4. 这里有两种可能
		4.1 第一种，如果断言的类型 T 是一个具体类型，然后类型断言检查 i 的动态类型是否和 T 相同。
			如果这个检查成功了，类型断言的结果是 i 的动态值，当然它的类型是 T。
		4.2 如果相反断言的类型 T 是一个接口类型，然后类型断言检查是否 i 的动态类型满足 T。如果这个检查成功了，动态值没有获取到；
			这个结果仍然是一个有相同类型和值部分的接口值，但是结果有类型 T。
	5. 如果 i 没有完全实现 T 接口的方法，这个语句将会触发宕机。触发宕机不是很友好，因此上面的语句还有一种写法：
		t,ok := i.(T)
		这种写法下，如果发生接口未实现时，将会把 ok 置为 false，t 置为 T 类型的 0 值。
		正常实现时，ok 为 true。这里 ok 可以被认为是：i 接口是否实现 T 类型的结果。
*/

/* demo1: 将接口转换为其他接口 */
// 定义飞行动物接口
type flyer interface {
	Fly()
}

// 定义行走动物接口
type walker interface {
	Walk()
}

// 鸟类结构体
type bird struct{}

// 实现 flyer 接口
func (b *bird) Fly() {
	fmt.Println("bird: fly")
}

// 实现 walker 接口
func (b *bird) Walk() {
	fmt.Println("bird: walk")
}

// 猪类结构体
type pig struct{}

// 实现 waler 接口
func (p *pig) Walk() {
	fmt.Println("pig: walk")
}

// TestDemo49 接口和类型之间的转换
func TestDemo49() {
	/*
		demo1:  将接口转换为其他接口
		1. 实现某个接口的类型同时实现了另外一个接口，此时可以在两个接口间转换。
	*/
	// 创建动物的名字到实例的映射
	animals := map[string]interface{}{
		"bird": new(bird),
		"pig":  new(pig),
	}

	// 遍历映射
	for name, obj := range animals {

		// 判断是否是 flyer
		f, isFlyer := obj.(flyer)
		// 判断是否是 walker
		w, isWalker := obj.(walker)

		fmt.Printf("name: %s, isFlyer: %v, isWalker: %v\n", name, isFlyer, isWalker)

		// 如果是 flyer, flyer.Fly() 方法
		if isFlyer {
			f.Fly()
		}
		// 如果是 walker, walker.Walk() 方法
		if isWalker {
			w.Walk()
		}
	}

	/*
		demo2: 将接口转换为其他类型
			1. 接口和其他类型的转换可以在 Go 语言中自由进行，前提是已经完全实现。
			2. 接口断言类似于流程控制中的 if。但大量类型断言出现时，应使用更为高效的类型分支 switch 特性。
	*/
	p1 := new(pig)

	var a walker = p1
	p2 := a.(*pig)

	// p1 和 p2 指针是相同的。
	fmt.Printf("p1=%p, p2=%p\n", p1, p2)

	// 报错: panic: interface conversion: demo4.walker is *demo4.pig, not *demo4.bird
	// 接口在转换为其他类型时，接口内保存的实例对应的类型指针，必须是要转换的对应的类型指针。
	// p3 := a.(*bird)

	// fmt.Println(p3)
}
